<repo_name>nizulzaim/pcpart<file_sep>/packages/vue-component-dev-client/package.js
Package.describe({
  name: 'akryum:vue-component-dev-client',
  version: '0.2.11',
  summary: 'Hot-reloading client for vue components',
  git: 'https://github.com/Akryum/meteor-vue-component',
  documentation: 'README.md',
  debugOnly: true
});

Package.onUse(function(api) {
  api.versionsFrom('1.3.3');
  api.use('ecmascript');
  api.use('reload');
  api.use('autoupdate');
  api.use('reactive-var');
  api.mainModule('client/dev-client.js', 'client');
});


Npm.depends({
  'socket.io-client': '1.4.6'
});
<file_sep>/imports/model/Guide.js
import {Class} from 'meteor/jagi:astronomy';
import {Meteor} from "meteor/meteor";
import {Product} from "./Product";
import {GuideLove} from "./GuideLove";
import {GuideComment} from "./GuideComment";
import {User} from "./User";
import {Manufacturer} from "./Manufacturer";
import {Images} from "./Images";
import {Seller} from "./Seller";


export const Guide = Class.create({
    name: "Guide",
    collection: new Mongo.Collection("guides"),
    fields: {
        title: {type: String},
        author: {type: String, optional: true},
        GeneralDescription: {type: String},
        CpuAndCoolerDescription: {type: String, optional: true},
        MotherboardDescription: {type: String, optional: true},
        MemoryDescription: {type: String, optional: true},
        StorageDescription: {type: String, optional: true},
        VideocardDescription: {type: String, optional: true},
        PSUDescription: {type: String, optional: true},
        CaseDescription: {type: String, optional: true},
        element: [String],
        love: {
            type: [String],
            default: [],
            optional : true,
        },
        randomSeed: {
            type: Number,
            optional: true,
        }
    },
    behaviors: {
        timestamp: {
            hasCreatedField: true,
            createdFieldName: 'createdAt',
            hasUpdatedField: true,
            updatedFieldName: 'updatedAt'
        },
        softremove: {
            removedFieldName: 'removed',
            hasRemovedAtField: true,
            removedAtFieldName: 'removedAt'
        }
    },
    helpers: {
        products() {
            return Product.find({_id: {$in: this.element}});
        },
        authorUser() {
            return User.findOne(this.author);
        },
        image() {
            let p = null;
            if (this.products() && this.products().count() > 0) {
                p = this.products();
                let res = [];
                p.forEach((item) => {
                    if (item.getImageLink()) res.push(item.getImageLink());
                });

                return res[0];
            }

            return "";
        }
    }
});

if (Meteor.isServer) {
    Guide.extend({
        meteorMethods: {
            create(obj, element) {
                let el = element.map((item)=> item.id);
                obj.element = el;
                obj.author = Meteor.userId();
                this.set(obj, {merge: true, cast: true});
                return this.save();
            },
            love() {
                let indexOf = this.love.indexOf(Meteor.userId());

                if (indexOf > -1) {
                    this.love.splice(indexOf,1);
                } else {
                    this.love.push(Meteor.userId());
                }

                return this.save();
            },
            removeGuide() {
                return this.softRemove();
            }
        },
        events: {
            afterInit(e) {
                
            }
        }
    });

    Meteor.publishComposite('guides', function(id) {
        return {
            find: function() {
                if (id) {
                    return Guide.find(id);
                }
                return Guide.find();
            },
            children: [
                {
                    find(guide) {
                        
                        return Product.find({_id: {$in: guide.element}});
                    },
                    children:[
                        {
                            find: function(p) {
                                return Manufacturer.find(p.manufacturerId);
                            }
                        },
                        {
                            find: function(p) {
                                return Images.find(p.imageId).cursor;
                            }
                        },
                        {
                            find: function(p) {
                                return Seller.find({productId: p._id});
                            }
                        },
                    ],
                }, {
                    find(guide) {
                        return GuideLove.find({guideId: guide._id, userId: this.userId});
                    },
                    
                },
                {
                    find(guide) {
                        return User.find(guide.author);
                    },
                    children:[
                        {
                            find(user) {
                                if (user.profile.imageId) {
                                    return Images.find(user.profile.imageId).cursor;
                                }

                                return undefined;
                            }
                        }
                    ],
                },
                
                
            ]
        };
    });

    Meteor.publishComposite('guideDetails', function(id) {
        return {
            find: function() {
                if (id) {
                    return Guide.find(id);
                }
                return Guide.find();
            },
            children: [
                {
                    find(guide) {
                        
                        return Product.find({_id: {$in: guide.element}});
                    },
                    children:[
                        {
                            find: function(p) {
                                return Manufacturer.find(p.manufacturerId);
                            }
                        },
                        {
                            find: function(p) {
                                return Images.find(p.imageId).cursor;
                            }
                        },
                        {
                            find: function(p) {
                                return Seller.find({productId: p._id});
                            }
                        },
                    ],
                }, {
                    find(guide) {
                        return GuideLove.find({guideId: guide._id, userId: this.userId});
                    },
                    
                },
                {
                    find(guide) {
                        return User.find(guide.author);
                    },
                    children:[
                        {
                            find(user) {
                                if (user.profile.imageId) {
                                    return Images.find(user.profile.imageId).cursor;
                                }

                                return undefined;
                            }
                        }
                    ],
                },
                {
                    find(guide) {
                        return GuideComment.find({guideId: guide._id});
                    },
                    children: [
                        {
                            find(gc) {
                                return User.find(gc.userId);
                            },
                            children:[
                                {
                                    find(user) {
                                        if (user.profile.imageId) {
                                            return Images.find(user.profile.imageId).cursor;
                                        }

                                        return undefined;
                                    }
                                }
                            ],
                        }
                    ]
                }
                
            ]
        };
    });
}<file_sep>/imports/model/Product.js
import {
    Class
} from 'meteor/jagi:astronomy';
import {
    Meteor
} from "meteor/meteor";

import {Manufacturer} from "./Manufacturer";
import {Images} from "./Images";
import {Seller} from "./Seller";

export const Product = Class.create({
    name: "Product",
    typeField: 'type',
    collection: new Mongo.Collection("products"),
    fields: {
        name: {type: String},
        manufacturerId: String,
        partNo: {type: String},
        imageId: {type: String},
        // model: Number,
    },
    behaviors: {
        timestamp: {
            hasCreatedField: true,
            createdFieldName: 'createdAt',
            hasUpdatedField: true,
            updatedFieldName: 'updatedAt'
        },
        softremove: {
            removedFieldName: 'removed',
            hasRemovedAtField: true,
            removedAtFieldName: 'removedAt'
        }
    },
    helpers: {
        manufacturer() {
            return Manufacturer.findOne(this.manufacturerId);
        },
        getImageLink() {
            let image = Images.findOne(this.imageId);
            if (!image) {
                return "";
            }
            return image._downloadRoute + "/images/" + image._id + "/original/" + image._id + "." + image.extension;
        },
        lowestPrice() {
            let sellers = Seller.find({productId: this._id});
            if (sellers && sellers.count() !== 0) {
                let s = sellers.fetch();
                s.sort((a, b) => {
                    return a.price - b.price;
                })

                return s[0].price;
            }

            return null;
        }
    }
});

export const Cpu = Product.inherit({
    name: 'Cpu',
    fields: {
        dataWidth: Number,
        socket: String,
        operatingFrequency: String,
        maxTurboFrequency: String,
        cores: Number,
        l1Cache: String,
        l2Cache: String,
        l3Cache: String,
        lithography: Number,
        thermalDesignPower: Number,
        includeCpuCooler: Boolean,
        hyperThreading: Boolean,
        maximumSupportedMemory: Number,
        integratedGraphics: String,
    }
});


export const Cpucooler = Product.inherit({
    name: 'Cpucooler',
    fields: {
        socket: String,
        liquidCooled: Boolean,
        bearingType: String,
        minNoiseLevel: Number,
        maxNoiseLevel: Number,
        minFan: Number,
        maxFan: Number,
        height: Number,
    },
    helpers: {
        noiseLevel() {
            if (this.minNoiseLevel) {
                return this.minNoiseLevel + " - " + this.maxNoiseLevel + " dB";
            }

            return this.maxNoiseLevel + " dB";
        },
        fanSpeed() {
            if (this.minFan) {
                return this.minFan + " - " + this.maxFan + " rpm";
            }
            return this.maxFan + " rpm";
        }
    }
});

export const Motherboard = Product.inherit({
    name: 'Motherboard',
    fields: {
        formFactor: String,
        socket: String,
        chipset: String,
        memorySlot: String,
        memoryType: String,
        maximumSupportedMemory: Number,
        raidSupport: Boolean,
        onboardVideo: Boolean,
        crossFireSupport: Boolean,
        sliSupport: String,
        sata6gb: Number,
        sataExpress: Number,
        onboardEthernet: String,
        onboardUsb3: Boolean,
    }
});


export const Memory = Product.inherit({
    name: 'Memory',
    fields: {
        memoryType: String,
        speed: String,
        size: String,
        pricePerGb: Number,
        casLatency: Number,
        timing: String,
        voltage: String,
        heatSpreader: Boolean,
        ecc: Boolean,
        registered: Boolean,
        color: String,
    }
});

export const Storage = Product.inherit({
    name: 'Storage',
    fields: {
        capacity: Number,
        interface: String,
        cache: String,
        rpm: Number,
        formFactor: String,
    }
});


export const Gpu = Product.inherit({
    name: 'Gpu',
    fields: {
        interface: String,
        chipset: String,
        memorySize: Number,
        memoryType: String,
        coreClock: Number,
        boostClock: Number,
        tdp: Number,
        fan: Boolean,
        sliSupport: Boolean,
        crossFireSupport: Boolean,
        sizeLength: Number,
        dviDualLink: Number,
        hdmi: Number,
        vga: Number,
        displayPort: Number,
    }
});

export const Psu = Product.inherit({
    name: 'Psu',
    fields: {
        psuType: String,
        wattage: Number,
        fans: Number,
        modular: String,
        output: String,
        efficiencyCert: String,
        pcieConnector: Number,
    }
});

export const Case = Product.inherit({
    name: 'Case',
    fields: {
        caseType: String,
        color: String,
        includePowerSupply: Boolean,
        external35Bays: Number,
        internal35Bays: Number,
        internal25Bays: Number,
        external25Bays: Number,
        internal525Bays: Number,
        external525Bays: Number,
        motherboardCompatibility: String,
        frontUsb3ports: Boolean,
        maximumVideoCardLength: Number,
        width: Number,
        sizeLength: Number,
        height: Number,
    },
    helpers: {
        includePowerSupplyString() {
            return this.includePowerSupply ? "Yes": "No";
        },
        dimensions() {
            return this.width + "mm x " + this.sizeLength + "mm x " + this.height + "mm";
        }
    }
});

if (Meteor.isServer) {
    Product.extend({
        meteorMethods: {
            create(general, specific) {
                this.set(general, {
                    merge: true,
                    cast: true,
                });
                this.set(specific, {
                    merge: true,
                    cast: true,
                });
                return this.save();
            },
            update(profileObj) {
                this.set({
                    profile: profileObj,
                }, { merge: true });
                console.log(this.save());
            }
        }
    });
    let children = [
        {
            find: function(p) {
                return Manufacturer.find(p.manufacturerId);
            }
        },
        {
            find: function(p) {
                return Images.find(p.imageId).cursor;
            }
        },
        {
            find: function(p) {
                return Seller.find({productId: p._id});
            }
        },
    ];

    Meteor.publishComposite('products', function(type ="", id="") {
        return {
            find: function() {
                if (id) {
                    return Product.find(id);
                }
                if (type) {
                    return Product.find({type});
                }
                return Product.find();
            },
            children,
        };
    });

    Meteor.publishComposite('productsByArrayOfID', function(arrayId) {
        return {
            find: function() {
                return Product.find({_id: {$in: arrayId}});
            },
            children,
        };
    });

    Meteor.publishComposite('productsSearch', function(type="", search = "",limit = 10) {
        return {
            find: function() {
                if (search) {
                    return Product.find({type, name: {'$regex' : search, '$options' : 'i'}}, {limit});
                }
                return Product.find({type}, {limit});
            },
            children,
        };
    });
}<file_sep>/imports/model/Manufacturer.js
import {
    Class
} from 'meteor/jagi:astronomy';
import {
    Meteor
} from "meteor/meteor";


export const Manufacturer = Class.create({
    name: "Manufacturer",
    collection: new Mongo.Collection("manufacturers"),
    fields: {
        name: String,
    },
    behaviors: {
        timestamp: {
            hasCreatedField: true,
            createdFieldName: 'createdAt',
            hasUpdatedField: true,
            updatedFieldName: 'updatedAt'
        },
        softremove: {
            removedFieldName: 'removed',
            hasRemovedAtField: true,
            removedAtFieldName: 'removedAt'
        }
    },
});

if (Meteor.isServer) {
    Manufacturer.extend({
        meteorMethods: {
            create(name) {
                this.name = name;
                return this.save();
            },
        }
    });

    Meteor.publishComposite('manufacturers', function(refresh = 0) {
        return {
            find: function() {
                return Manufacturer.find();
            },
        };
    });

    if (!Manufacturer.findOne()) {
        let m = new Manufacturer();
        m.name = "Intel";
        m.save();

        m = new Manufacturer();
        m.name = "AMD";
        m.save();

        m = new Manufacturer();
        m.name = "Cooler Master";
        m.save();
    }

}<file_sep>/imports/model/Seller.js
import {
    Class
} from 'meteor/jagi:astronomy';
import {
    Meteor
} from "meteor/meteor";



export const Seller = Class.create({
    name: "Seller",
    collection: new Mongo.Collection("sellers"),
    fields: {
        userId: String,
        productId: String,
        link: String,
        websiteType: Number,
        price: Number,
    },
    behaviors: {
        timestamp: {
            hasCreatedField: true,
            createdFieldName: 'createdAt',
            hasUpdatedField: true,
            updatedFieldName: 'updatedAt'
        },
        softremove: {
            removedFieldName: 'removed',
            hasRemovedAtField: true,
            removedAtFieldName: 'removedAt'
        }
    },
    helpers: {
        websiteName() {
            let res = "";
            res = this.websiteType === 0? "Amazon": res;
            res = this.websiteType === 1? "Lazada": res;
            res = this.websiteType === 2? "Lelong": res;
            res = this.websiteType === 3? "IP Mart": res;

            return res;
        }
    }
});

if (Meteor.isServer) {
    const possibleHostName = [
        "amazon.com",
        "lazada.com.my",
    ];

    Seller.extend({
        meteorMethods: {
            create(obj) {
                this.set(obj, {
                    merge: true,
                    cast: true,
                });
                let extractHostname = function (url) {
                    var hostname;
                    if (url.indexOf("://") > -1) {
                        hostname = url.split('/')[2];
                    }
                    else {
                        hostname = url.split('/')[0];
                    }
                    hostname = hostname.split(':')[0];
                    return hostname;
                };


                console.log(extractHostname(obj.link));

                this.userId = Meteor.userId();
                return this.save();
            },
        }
    });

    Meteor.publishComposite('sellers', function(productId = "") {
        return {
            find: function() {
                return Seller.find({productId});
            },
        };
    });

}<file_sep>/client/routes/index.routes.js
import {IndexNavigation, IndexToolbar} from "/imports/client/template";
import IndexPage from '/imports/client/views/public/Index.vue';
import Product from '/imports/client/views/public/Product.vue';
import ProductDetails from '/imports/client/views/public/ProductDetails.vue';
import ProductCompare from '/imports/client/views/public/ProductCompare.vue';
import Guide from '/imports/client/views/public/Guide.vue';
import Guides from '/imports/client/views/public/Guides.vue';

let routesParent = "";
export default [{
    path: `${routesParent}/`,
    name: "Index",
    meta: { fixToolbar: true, pageTitle: "PC Part" },
    components: {
        default: IndexPage,
        toolbar: IndexToolbar,
        navigation: IndexNavigation,
    }
},{
    path: `${routesParent}/product/compare`,
    name: "ProductCompare",
    meta: { fixToolbar: true,depth: 1, pageTitle: "Product Comparison" },
    components: {
        default: ProductCompare,
        toolbar: IndexToolbar,
        navigation: IndexNavigation,
    }
}, {
    path: `${routesParent}/product/:type`,
    name: "Products",
    meta: { fixToolbar: true,depth: 1, pageTitle: "Parts" },
    components: {
        default: Product,
        toolbar: IndexToolbar,
        navigation: IndexNavigation,
    }
}, {
    path: `${routesParent}/product/details/:id`,
    name: "ProductDetails",
    meta: { fixToolbar: true,depth: 1, pageTitle: "Product Details" },
    components: {
        default: ProductDetails,
        toolbar: IndexToolbar,
        navigation: IndexNavigation,
    }
},{
    path: `${routesParent}/guides/:id`,
    name: "Guide",
    meta: { fixToolbar: true,depth: 1, pageTitle: "Guide" },
    components: {
        default: Guide,
        toolbar: IndexToolbar,
        navigation: IndexNavigation,
    }
},{
    path: `${routesParent}/guides/`,
    name: "Guides",
    meta: { fixToolbar: true,depth: 1, pageTitle: "Guides List" },
    components: {
        default: Guides,
        toolbar: IndexToolbar,
        navigation: IndexNavigation,
    }
},];
<file_sep>/imports/client/template.js
export IndexToolbar from '/imports/client/views/templates/toolbar/IndexToolbar.vue';
export IndexNavigation from '/imports/client/views/templates/navigation/IndexNavigation.vue';<file_sep>/client/routes/admin.routes.js
import {IndexNavigation, IndexToolbar} from "/imports/client/template";
import ProductAdd from '/imports/client/views/public/dashboard/admin/ProductAdd.vue';

let routesParent = "/dashboard";
export default [{
    path: `${routesParent}/product/add`,
    name: "ProductAdd",
    meta: { fixToolbar: true, depth: 1, pageTitle: "Add Product" },
    components: {
        default: ProductAdd,
        toolbar: IndexToolbar,
        navigation: IndexNavigation,
    }
}, ];<file_sep>/imports/model/User.js
import {
    Class
} from 'meteor/jagi:astronomy';
import {
    Meteor
} from "meteor/meteor";

import {Images} from '/imports/model/Images.js';

export const UserProfile = Class.create({
    name: 'UserProfile',
    fields: {
        firstname: String,
        lastname: String,
        email: String,
        imageId: {type: String, optional: true},
        userType: {
            type: [Number],
            default: [2],
        },
    }
});

export const User = Class.create({
    name: "User",
    collection: Meteor.users,
    fields: {
        username: String,
        profile: UserProfile,
    },
    behaviors: {
        timestamp: {
            hasCreatedField: true,
            createdFieldName: 'createdAt',
            hasUpdatedField: true,
            updatedFieldName: 'updatedAt'
        },
        softremove: {
            removedFieldName: 'removed',
            hasRemovedAtField: true,
            removedAtFieldName: 'removedAt'
        }
    },
    helpers: {
        isAdmin() {
            return this.profile.userType.indexOf(0) > -1;
        },
        isSaler() {
            return this.profile.userType.indexOf(1) > -1;
        },
        isCustomer() {
            return this.profile.userType.indexOf(2) > -1;
        },
        image() {
            if(!this.profile.imageId) {
                return null;
            }
            return Images.findOne(this.profile.imageId);
        }
    }
});

if (Meteor.isServer) {
    User.extend({
        meteorMethods: {
            create(userObj, profileObj = {}) {
                userObj.profile = profileObj;
                Accounts.createUser(userObj);
            },
            update(username,profileObj) {
                this.username = username;
                this.set({
                    profile: profileObj,
                }, { merge: true });
                return this.save();
            }
        }
    });

    Meteor.publishComposite('loginUser', function(refresh = 0) {
        return {
            find: function() {
                return User.find(this.userId);
            },
            children:[
                {
                    find(user) {
                        if (user.profile.imageId) {
                            return Images.find(user.profile.imageId).cursor;
                        }

                        return undefined;
                    }
                }
            ],
        };
    });

    if (!User.findOne()) {
        let user = new User();
        user.create({
            username: "admin",
            password: "n",
        }, {
            firstname: "Admin",
            lastname: "Account",
            userType: [0],
            email: "<EMAIL>",
        });
    }

}<file_sep>/packages/vue-component-dev-client/client/dev-client.js
import "./buffer";
import Vue from 'vue'
import { Reload } from 'meteor/reload'
import { Meteor } from 'meteor/meteor'
import { Tracker } from 'meteor/tracker'
import { Autoupdate } from 'meteor/autoupdate'
import { ReactiveVar } from 'meteor/reactive-var'
import VueHot1 from './vue-hot'
import VueHot2 from './vue2-hot'

let VueHotReloadApi
const vueVersion = parseInt(Vue.version.charAt(0))

console.log('[HMR] Vue', Vue.version)
if (vueVersion === 1) {
  VueHotReloadApi = VueHot1
} else if (vueVersion === 2) {
  VueHotReloadApi = VueHot2
}

Vue.use(VueHotReloadApi)

// Hot reload API
window.__vue_hot__ = VueHotReloadApi

// Records initialized before vue hot reload api
if (window.__vue_hot_pending__) {
  for (let hash in window.__vue_hot_pending__) {
    VueHotReloadApi.createRecord(hash, window.__vue_hot_pending__[hash])
  }
}

// Reload override
var _suppressNextReload = false, _deferReload = 0
var _reload = Reload._reload
Reload._reload = function (options) {
  // Disable original reload for autoupdate package
  if (Reload._reload.caller.name !== '' && Reload._reload.caller.name !== 'checkNewVersionDocument') {
    _reload(options)
  }
}

// Custom reload method
function reload (options) {
  console.log('[HMR] Reload request received')
  if (_deferReload !== 0) {
    setTimeout(_reload, _deferReload)
    console.log(`[HMR] Client reload defered, will reload in ${_deferReload} ms`)
  } else if (_suppressNextReload) {
    console.log(`[HMR] Client version changed, you may need to reload the page`)
  } else {
    console.log(`[HMR] Reloading app...`)
    _reload.call(Reload, options)
  }
  _suppressNextReload = false
  _deferReload = 0
}

// Reimplement client version check from autoupdate package
var autoupdateVersion = __meteor_runtime_config__.autoupdateVersion || `unknown`
var ClientVersions = Autoupdate._ClientVersions
if (ClientVersions) {
  function checkNewVersionDocument (doc) {
    if (doc._id === 'version' && doc.version !== autoupdateVersion) {
      reload()
    }
  }
  ClientVersions.find().observe({
    added: checkNewVersionDocument,
    changed: checkNewVersionDocument,
  })
} else {
  console.log('[HMR] ClientVersions collection is not available, the app may full reload.')
}

// Hack https://github.com/socketio/socket.io-client/issues/961
import Response from 'meteor-node-stubs/node_modules/http-browserify/lib/response'
if (!Response.prototype.setEncoding) {
  Response.prototype.setEncoding = function (encoding) {
        // do nothing
  }
}

Meteor.startup(function () {
  // Dev client
  let devUrl = __meteor_runtime_config__.VUE_DEV_SERVER_URL

  console.log('[HMR] Dev client URL', devUrl)

  // NOTE: Socket lib don't allow mix HTTP and HTTPS servers URLs on client!
  let _socket = require('socket.io-client')(devUrl)
  window.__dev_client__ = _socket

  _socket.on('connect', function () {
    console.log('[HMR] Dev client connected')
  })
  _socket.on('disconnect', function () {
    console.log('[HMR] Dev client disconnected')
  })

  // JS
  _socket.on('js', Meteor.bindEnvironment(function ({hash, js, template, path}) {
    let args = ['vue']
    let regResult
    let error = null
    while (regResult = jsImportsReg.exec(js)) {
      args.push(regResult[2])
    }
    args.push(function (require, exports, module) {
      try {
        eval(js)
      } catch (e) {
        console.error(e)
        error = e
      }
    })

    let id = `${path + Math.round(new Date().getTime())}.js`
    const files = id.split('/')
    const fileObj = {}
    let currentObj = fileObj
    const indexMax = files.length - 1
    files.forEach((file, index) => {
      if (index === indexMax) {
        currentObj[file] = args
      } else {
        currentObj = currentObj[file] = {}
      }
    })

    let require = meteorInstall(fileObj)
    let result = require('./' + id)

    let needsReload = false
    if (!error) {
      console.log('[HMR] Reloading ' + path)
      if (!result.default.render && !template) {
        error = true
      }
      if (!error) {
        try {
          if (vueVersion === 1) {
            needsReload = VueHotReloadApi.update(hash, result.default, template)
          } else if (vueVersion === 2) {
            needsReload = VueHotReloadApi.reload(hash, result.default, template)
          }
        } catch (e) {
          console.error(e)
          error = true
        }
      }
    }

    _suppressNextReload = !error && !needsReload
  }))

  // Template
  _socket.on('render', function ({hash, template, path}) {
    if (vueVersion === 2) {
      console.log('[HMR] Rerendering ' + path)
      let error = false
      try {
        var obj
        eval(`obj = ${template};`)
        // console.log(obj)
        VueHotReloadApi.rerender(hash, obj)
      } catch (e) {
        error = true
      }
      _suppressNextReload = !error
    }
  })

  // CSS
  let _styleNodes = {}
  _socket.on('css', function ({hash, css, path}) {
    // console.log('css', hash, css.length);
    let style = _styleNodes[hash]
    if (!style) {
      style = document.createElement('style')
      style.setAttribute('data-v-source-file', path)
      document.getElementsByTagName('head')[0].appendChild(style)
      _styleNodes[hash] = style
    }
    style.textContent = css
  })

  // Locale
  _socket.on('lang.updated', function ({lang, data}) {
    Vue.locale(lang, data)
    if (lang === Vue.config.lang) {
      // Refresh
      VueHotReloadApi.updateWatchers()
    }
    console.log(`[HMR] Updated locale ${lang}`)
    _suppressNextReload = true
  })
  _socket.on('langs.updated', function ({langs}) {
    _deferReload = 3000
  })

  // Message
  _socket.on('message', function ({ type, message }) {
    let func
    if (type === 'error') {
      func = console.error
    } else if (type === 'warn') {
      func = console.warn
    } else {
      func = console.log
    }
    func(`[HMR] ${message}`)
  })

  // Reg
  const jsImportsReg = /module\.import\((['"])(.+?)\1/g
})
<file_sep>/imports/model/GuideComment.js
import {Class} from 'meteor/jagi:astronomy';
import {Meteor} from "meteor/meteor";
import {User} from "./User";
import {Images} from "./Images";

export const GuideComment = Class.create({
    name: "GuideComment",
    collection: new Mongo.Collection("guidecomments"),
    fields: {
        text: String,
        guideId: String,
        userId: String,
        root: {
            type: String,
            optional: true,
            default: 0,
        }
    },
    behaviors: {
        timestamp: {
            hasCreatedField: true,
            createdFieldName: 'createdAt',
            hasUpdatedField: true,
            updatedFieldName: 'updatedAt'
        },
        softremove: {
            removedFieldName: 'removed',
            hasRemovedAtField: true,
            removedAtFieldName: 'removedAt'
        }
    },
    helpers: {
        user() {
            return User.findOne(this.userId);
        }
    }
});

if (Meteor.isServer) {
    GuideComment.extend({
        meteorMethods: {
            create(text, guideId) {
                this.text = text;
                this.guideId = guideId;
                this.userId = Meteor.userId();
                return this.save();
            },
        }
    });

    Meteor.publishComposite('guidecomments', function(id) {
        return {
            find: function() {
                return GuideComment.find({guideId: id});
            },
            children: [
                {
                    find(gc) {
                        return User.find(gc.userId);
                    },
                    children:[
                        {
                            find(user) {
                                if (user.profile.imageId) {
                                    return Images.find(user.profile.imageId).cursor;
                                }

                                return undefined;
                            }
                        }
                    ],
                }
            ]
        };
    });
}