Skip to content

Commit

Permalink
Merge pull request #98 from JorgenVatle/meteor-v3
Browse files Browse the repository at this point in the history
Meteor v3
  • Loading branch information
JorgenVatle authored Jan 29, 2024
2 parents 8b309d6 + d003f10 commit 84585da
Show file tree
Hide file tree
Showing 21 changed files with 2,457 additions and 65 deletions.
5 changes: 5 additions & 0 deletions .idea/meteor-vite.iml

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

10 changes: 10 additions & 0 deletions .idea/runConfigurations/All_Tests.xml

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

2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ Use [Vite](https://vitejs.dev) in your Meteor app! ⚡️
- [**Solid.js**](https://www.solidjs.com/)
- [Example Project](/examples/solid)
- [Preview](https://solid--meteor-vite.wcaserver.com)
- Meteor v3 (Work in progress)
- Meteor v3 *(Experimental - Tested only with core packages)*
- [Example Project](/examples/meteor-v3-vue)
- [Preview](https://meteor-v3-vue--meteor-vite.wcaserver.com)

Expand Down
28 changes: 26 additions & 2 deletions examples/meteor-v3-vue/imports/ui/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,43 @@
<a :href="url">{{ title }}</a>
</li>
</ul>
<form @submit.prevent="linkForm.create()">
<label>
Link Title
<input v-model="linkForm.data.title" placeholder="Something important">
</label>
<label>
URL
<input v-model="linkForm.data.url" placeholder="https://example.com/...">
</label>
<button type="submit">
Submit
</button>
</form>
</div>
</div>
</template>

<script lang="ts" setup>
import { ref } from 'vue';
import { reactive, ref } from 'vue';
import LinksCollection, { LinkDocument } from '../api/links/links.collection';
import { Tracker } from 'meteor/tracker';
import { Meteor } from 'meteor/meteor';
const links = ref<LinkDocument[]>([]);
const ready = ref(false);
const linkForm = reactive({
data: {
title: '',
url: '',
},
async create() {
await Meteor.callAsync('links.create', linkForm.data);
}
})
Tracker.autorun(() => {
const subscription = Meteor.subscribe('links');
ready.value = subscription.ready();
links.value = LinksCollection.find({});
links.value = LinksCollection.find({}).fetch();
});
</script>
4 changes: 3 additions & 1 deletion examples/meteor-v3-vue/server/main.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { Meteor } from "meteor/meteor";
import { onPageLoad } from "meteor/server-render";
import LinksCollection from '../imports/api/links/links.collection';
import '../imports/api/links/links.methods';
import '../imports/api/links/server/links.publications';

Meteor.startup(async () => {
// Code to run on server startup.
Expand All @@ -11,7 +13,7 @@ Meteor.startup(async () => {
return;
}

LinksCollection.insertAsync({
await LinksCollection.insertAsync({
title: 'Meteor.com',
url: 'https://meteor.com',
});
Expand Down
2 changes: 1 addition & 1 deletion examples/vue/.meteor/versions
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ html-tools@1.1.3
htmljs@1.1.1
id-map@1.1.1
inter-process-messaging@0.1.1
jorgenvatle:vite-bundler@1.10.1
jorgenvatle:vite-bundler@1.12.0
launch-screen@1.3.0
logging@1.3.2
meteor@1.11.2
Expand Down
154 changes: 101 additions & 53 deletions npm-packages/meteor-vite/src/meteor/parser/Parser.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,32 @@
import { parse } from '@babel/parser';
import {
CallExpression, Expression, ExpressionStatement,
FunctionExpression, Identifier, is, MemberExpression,
Node, NumericLiteral,
ObjectExpression, ObjectMethod,
ObjectProperty, shallowEqual, StringLiteral,
traverse, VariableDeclarator,
is,
isCallExpression,
isFunctionExpression,
isIdentifier,
isMemberExpression,
isObjectExpression, isObjectProperty,
isReturnStatement,
isStringLiteral,
Node,
NumericLiteral,
ObjectExpression,
ObjectMethod,
ObjectProperty,
StringLiteral,
traverse,
VariableDeclarator,
} from '@babel/types';
import FS from 'fs/promises';
import { inspect } from 'util';
import Logger from '../../utilities/Logger';
import { MeteorViteError } from '../../error/MeteorViteError';
import Logger from '../../utilities/Logger';
import {
KnownModuleMethodNames,
MeteorInstallCallExpression,
MeteorPackageProperty,
ModuleMethod,
ModuleMethodName,
MeteorInstallObject, MeteorInstallCallExpression,
} from './ParserTypes';

interface ParseOptions {
Expand Down Expand Up @@ -113,59 +123,97 @@ function readMainModulePath(node: Node) {
return node.init.arguments[0].value;
}


function parsePackageScope(node: Node) {
if (node.type !== 'CallExpression') return;
if (node.callee.type !== 'MemberExpression') return;
const { object, property } = node.callee;
if (object.type !== 'Identifier') return;
if (object.name !== 'Package') return;
if (property.type !== 'Identifier') return;
if (property.name !== '_define') return;

const args = {
packageName: node.arguments[0],
moduleExports: node.arguments[1],
packageExports: node.arguments[2],
}

if (args.packageName.type !== 'StringLiteral') {
throw new ModuleExportsError('Unexpected type received for package name!', args.packageName);
}

/**
* Deals with the meteor/meteor core packages that don't use the module system.
*/
if (!args.packageExports && args.moduleExports?.type === 'ObjectExpression') {
args.packageExports = args.moduleExports;
}

const packageExport = {
name: args.packageName.value,
exports: [] as string[],
};

/**
* Module is likely a lazy-loaded package or one that only provides side effects as there are no exports in any
* form.
*/
if (!args.packageExports) {
function meteorV2(node: Node) {
if (node.type !== 'CallExpression') return;
if (node.callee.type !== 'MemberExpression') return;
const { object, property } = node.callee;
if (object.type !== 'Identifier') return;
if (object.name !== 'Package') return;
if (property.type !== 'Identifier') return;
if (property.name !== '_define') return;

const args = {
packageName: node.arguments[0],
moduleExports: node.arguments[1],
packageExports: node.arguments[2],
}

if (args.packageName.type !== 'StringLiteral') {
throw new ModuleExportsError('Unexpected type received for package name!', args.packageName);
}

/**
* Deals with the meteor/meteor core packages that don't use the module system.
*/
if (!args.packageExports && args.moduleExports?.type === 'ObjectExpression') {
args.packageExports = args.moduleExports;
}

const packageExport = {
name: args.packageName.value,
exports: [] as string[],
};

/**
* Module is likely a lazy-loaded package or one that only provides side effects as there are no exports in any
* form.
*/
if (!args.packageExports) {
return packageExport;
}

if (args.packageExports.type !== 'ObjectExpression') {
throw new ModuleExportsError('Unexpected type received for package-scope exports argument!', args.packageExports);
}

args.packageExports.properties.forEach((property) => {
if (property.type === 'SpreadElement') {
throw new ModuleExportsError('Unexpected property type received for package-scope exports!', property)
}

packageExport.exports.push(propParser.getKey(property));
})

return packageExport;
}

if (args.packageExports.type !== 'ObjectExpression') {
throw new ModuleExportsError('Unexpected type received for package-scope exports argument!', args.packageExports);
}

args.packageExports.properties.forEach((property) => {
if (property.type === 'SpreadElement') {
throw new ModuleExportsError('Unexpected property type received for package-scope exports!', property)
function meteorV3(node: Node) {
if (!isCallExpression(node)) return;
if (!isMemberExpression(node.callee)) return;
if (!isIdentifier(node.callee.property, { name: 'queue' })) return;
if (!isStringLiteral(node.arguments[0])) return;
if (!isFunctionExpression(node.arguments[2])) return;
const packageName = node.arguments[0].value;
const exports: string[] = [];

const packageFunction = node.arguments[2];
for (const node of packageFunction.body.body) {
if (!isReturnStatement(node)) continue;
if (!isObjectExpression(node.argument)) continue;
for (const property of node.argument.properties) {
if (!isObjectProperty(property)) continue;
if (!isIdentifier(property.key, { name: 'export' })) continue;
if (!isFunctionExpression(property.value)) continue;
const exportBody = property.value.body.body;
for (const node of exportBody) {
if (!isReturnStatement(node)) continue;
if (!isObjectExpression(node.argument)) continue;
node.argument.properties.forEach((node) => {
if (!isObjectProperty(node)) return;
if (!isIdentifier(node.key)) return;
exports.push(node.key.name);
});

}
}
}

packageExport.exports.push(propParser.getKey(property));
})
return { name: packageName, exports };
}

return packageExport;
return meteorV2(node) || meteorV3(node);
}

/**
Expand Down
12 changes: 10 additions & 2 deletions npm-packages/meteor-vite/test/Parser/AllMockPackages.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ import Path from 'path';
import { describe, expect, it, test } from 'vitest';
import MeteorPackage from '../../src/meteor/package/components/MeteorPackage';
import { parseMeteorPackage } from '../../src/meteor/parser/Parser';
import { AllMockPackages } from '../__mocks';
import { AllMockPackages_MeteorV2, AllMockPackages_MeteorV3 } from '../__mocks';

describe('Validate known exports for mock packages', () => {

describe.each(AllMockPackages)(`meteor/$packageName`, async (mockPackage) => {
describe.each([...AllMockPackages_MeteorV2, ...AllMockPackages_MeteorV3])(`[Meteor v$meteorVersion] meteor/$packageName`, async (mockPackage) => {
const { result: parsedPackage } = await parseMeteorPackage({
filePath: mockPackage.filePath,
fileContent: mockPackage.fileContent,
Expand Down Expand Up @@ -38,6 +38,14 @@ describe('Validate known exports for mock packages', () => {
}

expect(mainModuleExports).toEqual(mockModuleExports);
});

it('has export keys for all package-scope exports', () => {
const meteorPackage = new MeteorPackage(parsedPackage, { timeSpent: 'none' }).serialize({});

Object.values(mockPackage.packageScopeExports).flat().forEach((exportKey) => {
expect(meteorPackage.exportKeys).toContain(exportKey);
})
})
})

Expand Down
Loading

0 comments on commit 84585da

Please sign in to comment.