mirror of
https://github.com/tobspr/shapez.io.git
synced 2025-06-13 13:04:03 +00:00
[WIP] Add boilerplate for achievement implementation
This commit is contained in:
parent
14d09a7d52
commit
7c69331308
4
.gitignore
vendored
4
.gitignore
vendored
@ -50,3 +50,7 @@ tmp_standalone_files
|
|||||||
# Local config
|
# Local config
|
||||||
config.local.js
|
config.local.js
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
|
||||||
|
# Editor artifacts
|
||||||
|
*.*.swp
|
||||||
|
*.*.swo
|
||||||
|
@ -12,6 +12,9 @@ import { getPlatformName, waitNextFrame } from "./core/utils";
|
|||||||
import { Vector } from "./core/vector";
|
import { Vector } from "./core/vector";
|
||||||
import { AdProviderInterface } from "./platform/ad_provider";
|
import { AdProviderInterface } from "./platform/ad_provider";
|
||||||
import { NoAdProvider } from "./platform/ad_providers/no_ad_provider";
|
import { NoAdProvider } from "./platform/ad_providers/no_ad_provider";
|
||||||
|
import { AchievementsInterface } from "./platform/achievements";
|
||||||
|
import { NoAchievements } from "./platform/achievements/no_achievements";
|
||||||
|
import { Achievements } from "./platform/achievements/achievements";
|
||||||
import { AnalyticsInterface } from "./platform/analytics";
|
import { AnalyticsInterface } from "./platform/analytics";
|
||||||
import { GoogleAnalyticsImpl } from "./platform/browser/google_analytics";
|
import { GoogleAnalyticsImpl } from "./platform/browser/google_analytics";
|
||||||
import { SoundImplBrowser } from "./platform/browser/sound";
|
import { SoundImplBrowser } from "./platform/browser/sound";
|
||||||
@ -85,6 +88,9 @@ export class Application {
|
|||||||
/** @type {PlatformWrapperInterface} */
|
/** @type {PlatformWrapperInterface} */
|
||||||
this.platformWrapper = null;
|
this.platformWrapper = null;
|
||||||
|
|
||||||
|
/** @type {AchievementsInterface} */
|
||||||
|
this.achievements = null;
|
||||||
|
|
||||||
/** @type {AdProviderInterface} */
|
/** @type {AdProviderInterface} */
|
||||||
this.adProvider = null;
|
this.adProvider = null;
|
||||||
|
|
||||||
@ -137,6 +143,7 @@ export class Application {
|
|||||||
this.sound = new SoundImplBrowser(this);
|
this.sound = new SoundImplBrowser(this);
|
||||||
this.analytics = new GoogleAnalyticsImpl(this);
|
this.analytics = new GoogleAnalyticsImpl(this);
|
||||||
this.gameAnalytics = new ShapezGameAnalytics(this);
|
this.gameAnalytics = new ShapezGameAnalytics(this);
|
||||||
|
this.achievements = new NoAchievements(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -3,10 +3,10 @@ export default {
|
|||||||
/* dev:start */
|
/* dev:start */
|
||||||
// -----------------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------------
|
||||||
// Quickly enters the game and skips the main menu - good for fast iterating
|
// Quickly enters the game and skips the main menu - good for fast iterating
|
||||||
// fastGameEnter: true,
|
fastGameEnter: true,
|
||||||
// -----------------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------------
|
||||||
// Skips any delays like transitions between states and such
|
// Skips any delays like transitions between states and such
|
||||||
// noArtificialDelays: true,
|
noArtificialDelays: true,
|
||||||
// -----------------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------------
|
||||||
// Disables writing of savegames, useful for testing the same savegame over and over
|
// Disables writing of savegames, useful for testing the same savegame over and over
|
||||||
// disableSavegameWrite: true,
|
// disableSavegameWrite: true,
|
||||||
@ -18,7 +18,7 @@ export default {
|
|||||||
// showAcceptorEjectors: true,
|
// showAcceptorEjectors: true,
|
||||||
// -----------------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------------
|
||||||
// Disables the music (Overrides any setting, can cause weird behaviour)
|
// Disables the music (Overrides any setting, can cause weird behaviour)
|
||||||
// disableMusic: true,
|
disableMusic: true,
|
||||||
// -----------------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------------
|
||||||
// Do not render static map entities (=most buildings)
|
// Do not render static map entities (=most buildings)
|
||||||
// doNotRenderStatics: true,
|
// doNotRenderStatics: true,
|
||||||
@ -59,6 +59,9 @@ export default {
|
|||||||
// Enables ads in the local build (normally they are deactivated there)
|
// Enables ads in the local build (normally they are deactivated there)
|
||||||
// testAds: true,
|
// testAds: true,
|
||||||
// -----------------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------------
|
||||||
|
// Allows unlocked achievements to be logged to console in the local build
|
||||||
|
testAchievements: true,
|
||||||
|
// -----------------------------------------------------------------------------------
|
||||||
// Disables the automatic switch to an overview when zooming out
|
// Disables the automatic switch to an overview when zooming out
|
||||||
// disableMapOverview: true,
|
// disableMapOverview: true,
|
||||||
// -----------------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------------
|
||||||
|
56
src/js/game/achievement_manager.js
Normal file
56
src/js/game/achievement_manager.js
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
/* typehints:start */
|
||||||
|
import { GameRoot } from "./root";
|
||||||
|
/* typehints:end */
|
||||||
|
|
||||||
|
import { globalConfig } from "../core/config";
|
||||||
|
import { createLogger } from "../core/logging";
|
||||||
|
|
||||||
|
const logger = createLogger("achievement_manager");
|
||||||
|
|
||||||
|
export class AchievementManager {
|
||||||
|
static getId() {
|
||||||
|
return "AchievementManager";
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(root) {
|
||||||
|
this.root = root;
|
||||||
|
this.achievements = null;
|
||||||
|
|
||||||
|
if (!this.root.app.achievements.hasAchievements()) {
|
||||||
|
logger.debug("Bypassing achievement set up");
|
||||||
|
// Set adhoc checks to reference a noop, ignore signals.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.init();
|
||||||
|
}
|
||||||
|
|
||||||
|
init () {
|
||||||
|
return this.root.app.achievements.load()
|
||||||
|
.then(() => {
|
||||||
|
this.achievements = this.root.app.achievements.getAchievements();
|
||||||
|
|
||||||
|
return this.setChecks();
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
setChecks () {
|
||||||
|
logger.debug("loaded", this.achievements);
|
||||||
|
|
||||||
|
// set checks on achievements
|
||||||
|
|
||||||
|
//this.root.signals.itemProduced.add(this.onItemProduced, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {BaseItem} item
|
||||||
|
*/
|
||||||
|
onItemProduced(item) {
|
||||||
|
logger.debug(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Have one check function per achievement
|
||||||
|
isPainted () {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
@ -35,6 +35,7 @@ import { RegularGameMode } from "./modes/regular";
|
|||||||
import { ProductionAnalytics } from "./production_analytics";
|
import { ProductionAnalytics } from "./production_analytics";
|
||||||
import { GameRoot } from "./root";
|
import { GameRoot } from "./root";
|
||||||
import { ShapeDefinitionManager } from "./shape_definition_manager";
|
import { ShapeDefinitionManager } from "./shape_definition_manager";
|
||||||
|
import { AchievementManager } from "./achievement_manager";
|
||||||
import { SoundProxy } from "./sound_proxy";
|
import { SoundProxy } from "./sound_proxy";
|
||||||
import { GameTime } from "./time/game_time";
|
import { GameTime } from "./time/game_time";
|
||||||
|
|
||||||
@ -118,6 +119,7 @@ export class GameCore {
|
|||||||
root.entityMgr = new EntityManager(root);
|
root.entityMgr = new EntityManager(root);
|
||||||
root.systemMgr = new GameSystemManager(root);
|
root.systemMgr = new GameSystemManager(root);
|
||||||
root.shapeDefinitionMgr = new ShapeDefinitionManager(root);
|
root.shapeDefinitionMgr = new ShapeDefinitionManager(root);
|
||||||
|
root.achievementMgr = new AchievementManager(root);
|
||||||
root.hubGoals = new HubGoals(root);
|
root.hubGoals = new HubGoals(root);
|
||||||
root.productionAnalytics = new ProductionAnalytics(root);
|
root.productionAnalytics = new ProductionAnalytics(root);
|
||||||
root.buffers = new BufferMaintainer(root);
|
root.buffers = new BufferMaintainer(root);
|
||||||
|
@ -8,6 +8,7 @@ import { createLogger } from "../core/logging";
|
|||||||
import { GameTime } from "./time/game_time";
|
import { GameTime } from "./time/game_time";
|
||||||
import { EntityManager } from "./entity_manager";
|
import { EntityManager } from "./entity_manager";
|
||||||
import { GameSystemManager } from "./game_system_manager";
|
import { GameSystemManager } from "./game_system_manager";
|
||||||
|
import { AchievementManager } from "./achievement_manager";
|
||||||
import { GameHUD } from "./hud/hud";
|
import { GameHUD } from "./hud/hud";
|
||||||
import { MapView } from "./map_view";
|
import { MapView } from "./map_view";
|
||||||
import { Camera } from "./camera";
|
import { Camera } from "./camera";
|
||||||
@ -119,6 +120,9 @@ export class GameRoot {
|
|||||||
/** @type {SoundProxy} */
|
/** @type {SoundProxy} */
|
||||||
this.soundProxy = null;
|
this.soundProxy = null;
|
||||||
|
|
||||||
|
/** @type {AchievementManager} */
|
||||||
|
this.achievementMgr = null;
|
||||||
|
|
||||||
/** @type {ShapeDefinitionManager} */
|
/** @type {ShapeDefinitionManager} */
|
||||||
this.shapeDefinitionMgr = null;
|
this.shapeDefinitionMgr = null;
|
||||||
|
|
||||||
|
28
src/js/platform/achievements.js
Normal file
28
src/js/platform/achievements.js
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
/* typehints:start */
|
||||||
|
import { Application } from "../application";
|
||||||
|
/* typehints:end */
|
||||||
|
|
||||||
|
export class AchievementsInterface {
|
||||||
|
constructor(app) {
|
||||||
|
/** @type {Application} */
|
||||||
|
this.app = app;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes the list of achievements
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
initialize() {
|
||||||
|
abstract;
|
||||||
|
return Promise.reject();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if achievements are supported in the current build
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
hasAchievements() {
|
||||||
|
abstract;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
79
src/js/platform/achievements/achievements.js
Normal file
79
src/js/platform/achievements/achievements.js
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
import { AchievementsInterface } from "../achievements";
|
||||||
|
import { globalConfig } from "../../core/config";
|
||||||
|
import { createLogger } from "../../core/logging";
|
||||||
|
import { newEmptyMap } from "../../core/utils";
|
||||||
|
//import { T } from "../../translations";
|
||||||
|
|
||||||
|
const logger = createLogger("achievements/default");
|
||||||
|
|
||||||
|
// Include API id per key
|
||||||
|
export const ACHIEVEMENTS = {
|
||||||
|
painting: "painting"
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Achievements extends AchievementsInterface {
|
||||||
|
initialize() {
|
||||||
|
this.authTicket = null;
|
||||||
|
this.achievementNames = null;
|
||||||
|
this.achievements = null;
|
||||||
|
this.connected = false;
|
||||||
|
this.connectPromise = Promise.resolve();
|
||||||
|
|
||||||
|
if (globalConfig.debug.testAchievements) {
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for resolve in AchievementManager via load() to not block game state
|
||||||
|
// transition
|
||||||
|
this.connectPromise = this.fetchAuthTicket()
|
||||||
|
.then(() => this.fetchAchievementNames());
|
||||||
|
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
|
||||||
|
fetchAuthTicket () {
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
|
||||||
|
fetchAchievementNames () {
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
|
||||||
|
load () {
|
||||||
|
this.achievements = newEmptyMap();
|
||||||
|
|
||||||
|
for (let key in ACHIEVEMENTS) {
|
||||||
|
this.achievements[key] = newEmptyMap();
|
||||||
|
this.achievements[key].unlocked = false;
|
||||||
|
this.achievements[key].invalid = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.connectPromise
|
||||||
|
.then(() => {
|
||||||
|
// factor in game state, save data, then Steam data (if accessible) as
|
||||||
|
// source of truth.
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} key
|
||||||
|
*/
|
||||||
|
fetchAchievement (key) {
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} key
|
||||||
|
*/
|
||||||
|
unlockAchievement (key) {
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
|
||||||
|
getAchievements() {
|
||||||
|
return this.achievements;
|
||||||
|
}
|
||||||
|
|
||||||
|
hasAchievements() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
11
src/js/platform/achievements/no_achievements.js
Normal file
11
src/js/platform/achievements/no_achievements.js
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import { AchievementsInterface } from "../achievements";
|
||||||
|
|
||||||
|
export class NoAchievements extends AchievementsInterface {
|
||||||
|
initialize() {
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
|
||||||
|
hasAchievements() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
@ -4,6 +4,7 @@ import { queryParamOptions } from "../../core/query_parameters";
|
|||||||
import { clamp } from "../../core/utils";
|
import { clamp } from "../../core/utils";
|
||||||
import { GamedistributionAdProvider } from "../ad_providers/gamedistribution";
|
import { GamedistributionAdProvider } from "../ad_providers/gamedistribution";
|
||||||
import { NoAdProvider } from "../ad_providers/no_ad_provider";
|
import { NoAdProvider } from "../ad_providers/no_ad_provider";
|
||||||
|
import { Achievements } from "../achievements/achievements";
|
||||||
import { PlatformWrapperInterface } from "../wrapper";
|
import { PlatformWrapperInterface } from "../wrapper";
|
||||||
import { StorageImplBrowser } from "./storage";
|
import { StorageImplBrowser } from "./storage";
|
||||||
import { StorageImplBrowserIndexedDB } from "./storage_indexed_db";
|
import { StorageImplBrowserIndexedDB } from "./storage_indexed_db";
|
||||||
@ -71,6 +72,7 @@ export class PlatformWrapperImplBrowser extends PlatformWrapperInterface {
|
|||||||
|
|
||||||
return this.detectStorageImplementation()
|
return this.detectStorageImplementation()
|
||||||
.then(() => this.initializeAdProvider())
|
.then(() => this.initializeAdProvider())
|
||||||
|
.then(() => this.initializeAchievements())
|
||||||
.then(() => super.initialize());
|
.then(() => super.initialize());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -196,6 +198,16 @@ export class PlatformWrapperImplBrowser extends PlatformWrapperInterface {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
initializeAchievements() {
|
||||||
|
if (G_IS_STANDALONE || (G_IS_DEV && globalConfig.debug.testAchievements)) {
|
||||||
|
this.app.achievements = new Achievements(this.app);
|
||||||
|
return this.app.achievements.initialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.log("Achievements are not supported in this environment");
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
|
||||||
exitApp() {
|
exitApp() {
|
||||||
// Can not exit app
|
// Can not exit app
|
||||||
}
|
}
|
||||||
|
@ -1207,6 +1207,11 @@ demo:
|
|||||||
|
|
||||||
settingNotAvailable: Not available in the demo.
|
settingNotAvailable: Not available in the demo.
|
||||||
|
|
||||||
|
achievements:
|
||||||
|
painting:
|
||||||
|
displayName: Painting
|
||||||
|
description: Paint a shape
|
||||||
|
|
||||||
tips:
|
tips:
|
||||||
- The hub will accept any input, not just the current shape!
|
- The hub will accept any input, not just the current shape!
|
||||||
- Make sure your factories are modular - it will pay out!
|
- Make sure your factories are modular - it will pay out!
|
||||||
|
35
yarn.lock
35
yarn.lock
@ -5103,14 +5103,6 @@ levn@^0.4.1:
|
|||||||
prelude-ls "^1.2.1"
|
prelude-ls "^1.2.1"
|
||||||
type-check "~0.4.0"
|
type-check "~0.4.0"
|
||||||
|
|
||||||
line-column@^1.0.2:
|
|
||||||
version "1.0.2"
|
|
||||||
resolved "https://registry.yarnpkg.com/line-column/-/line-column-1.0.2.tgz#d25af2936b6f4849172b312e4792d1d987bc34a2"
|
|
||||||
integrity sha1-0lryk2tvSEkXKzEuR5LR2Ye8NKI=
|
|
||||||
dependencies:
|
|
||||||
isarray "^1.0.0"
|
|
||||||
isobject "^2.0.0"
|
|
||||||
|
|
||||||
load-bmfont@^1.3.1, load-bmfont@^1.4.0:
|
load-bmfont@^1.3.1, load-bmfont@^1.4.0:
|
||||||
version "1.4.0"
|
version "1.4.0"
|
||||||
resolved "https://registry.yarnpkg.com/load-bmfont/-/load-bmfont-1.4.0.tgz#75f17070b14a8c785fe7f5bee2e6fd4f98093b6b"
|
resolved "https://registry.yarnpkg.com/load-bmfont/-/load-bmfont-1.4.0.tgz#75f17070b14a8c785fe7f5bee2e6fd4f98093b6b"
|
||||||
@ -5619,10 +5611,10 @@ nan@^2.12.1:
|
|||||||
resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.1.tgz#d7be34dfa3105b91494c3147089315eff8874b01"
|
resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.1.tgz#d7be34dfa3105b91494c3147089315eff8874b01"
|
||||||
integrity sha512-isWHgVjnFjh2x2yuJ/tj3JbwoHu3UC2dX5G/88Cm24yB6YopVgxvBObDY7n5xW6ExmFhJpSEQqFPvq9zaXc8Jw==
|
integrity sha512-isWHgVjnFjh2x2yuJ/tj3JbwoHu3UC2dX5G/88Cm24yB6YopVgxvBObDY7n5xW6ExmFhJpSEQqFPvq9zaXc8Jw==
|
||||||
|
|
||||||
nanoid@^3.1.12:
|
nanoid@^3.1.20:
|
||||||
version "3.1.12"
|
version "3.1.20"
|
||||||
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.12.tgz#6f7736c62e8d39421601e4a0c77623a97ea69654"
|
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.20.tgz#badc263c6b1dcf14b71efaa85f6ab4c1d6cfc788"
|
||||||
integrity sha512-1qstj9z5+x491jfiC4Nelk+f8XBad7LN20PmyWINJEMRSf3wcAjAWysw1qaA8z6NSKe2sjq1hRSDpBH5paCb6A==
|
integrity sha512-a1cQNyczgKbLX9jwbS/+d7W8fX/RfgYR7lVWwWOGIPNgK2m0MWvrGF6/m4kk6U3QcFMnZf3RIhL0v2Jgh/0Uxw==
|
||||||
|
|
||||||
nanomatch@^1.2.9:
|
nanomatch@^1.2.9:
|
||||||
version "1.2.13"
|
version "1.2.13"
|
||||||
@ -6927,6 +6919,15 @@ postcss-zindex@^4.0.1:
|
|||||||
postcss "^7.0.0"
|
postcss "^7.0.0"
|
||||||
uniqs "^2.0.0"
|
uniqs "^2.0.0"
|
||||||
|
|
||||||
|
postcss@>=5.0.0:
|
||||||
|
version "8.2.6"
|
||||||
|
resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.2.6.tgz#5d69a974543b45f87e464bc4c3e392a97d6be9fe"
|
||||||
|
integrity sha512-xpB8qYxgPuly166AGlpRjUdEYtmOWx2iCwGmrv4vqZL9YPVviDVPZPRXxnXr6xPZOdxQ9lp3ZBFCRgWJ7LE3Sg==
|
||||||
|
dependencies:
|
||||||
|
colorette "^1.2.1"
|
||||||
|
nanoid "^3.1.20"
|
||||||
|
source-map "^0.6.1"
|
||||||
|
|
||||||
postcss@^5.0.2:
|
postcss@^5.0.2:
|
||||||
version "5.2.18"
|
version "5.2.18"
|
||||||
resolved "https://registry.yarnpkg.com/postcss/-/postcss-5.2.18.tgz#badfa1497d46244f6390f58b319830d9107853c5"
|
resolved "https://registry.yarnpkg.com/postcss/-/postcss-5.2.18.tgz#badfa1497d46244f6390f58b319830d9107853c5"
|
||||||
@ -6955,16 +6956,6 @@ postcss@^7.0.0, postcss@^7.0.1, postcss@^7.0.14, postcss@^7.0.17, postcss@^7.0.2
|
|||||||
source-map "^0.6.1"
|
source-map "^0.6.1"
|
||||||
supports-color "^6.1.0"
|
supports-color "^6.1.0"
|
||||||
|
|
||||||
postcss@^8.1.1:
|
|
||||||
version "8.1.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.1.1.tgz#c3a287dd10e4f6c84cb3791052b96a5d859c9389"
|
|
||||||
integrity sha512-9DGLSsjooH3kSNjTZUOt2eIj2ZTW0VI2PZ/3My+8TC7KIbH2OKwUlISfDsf63EP4aiRUt3XkEWMWvyJHvJelEg==
|
|
||||||
dependencies:
|
|
||||||
colorette "^1.2.1"
|
|
||||||
line-column "^1.0.2"
|
|
||||||
nanoid "^3.1.12"
|
|
||||||
source-map "^0.6.1"
|
|
||||||
|
|
||||||
prelude-ls@^1.2.1:
|
prelude-ls@^1.2.1:
|
||||||
version "1.2.1"
|
version "1.2.1"
|
||||||
resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396"
|
resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396"
|
||||||
|
Loading…
Reference in New Issue
Block a user